package client.view;

import client.comms.PostData;
import client.model.Genre;
import client.model.Track;
import com.sun.xml.internal.ws.message.StringHeader;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import sun.awt.HorizBagLayout;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.*;
import javax.swing.border.BevelBorder;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.metal.MetalIconFactory;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Array;
import java.net.URL;
import java.util.ArrayList;
import java.util.Vector;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

    import java.util.regex.Matcher;
    import java.util.regex.Pattern;

/**
 * Created by IntelliJ IDEA.
 * User: sam
 * Date: 02/03/12
 * Time: 21:39
 * To change this template use File | Settings | File Templates.
 */
public class UploadPanel extends JPanel implements ActionListener, MouseListener {

    JPanel mainPanel;
    JScrollPane scrollPane;
    JPanel trackListPanel;
    JPanel bottomPanel;
    JPanel controlPanel;
    JComboBox genreList;
    JEditorPane outputTextArea;
    JFileChooser fileChooser;
    JRadioButton pattSplit1;
    JRadioButton pattSplit2;
    JTextField genreDescTextField;
    JTextField genreNameTextField;
    ButtonGroup radioBtnGroup;
    JProgressBar progressBar;

    JButton uploadButton;
    JButton cancelButton;

    DefaultTableModel tableModel;
    JTable jTable;
    Vector columns;

    //Vector<Vector<String>> uploadList = new Vector<Vector<String>>();
    Vector <String> uploadList = new Vector<String>();  // List of absolute paths

    DownloadControlPanel downloadControlPanel;

    String fileExts[] = {".mp3", ".wav", ".m4a", ".m4p", ".wma"};

    public UploadPanel() {
    }

    public void init(DownloadControlPanel downloadControlPanel) {

        this.downloadControlPanel = downloadControlPanel;
        Border bevelBorder = BorderFactory.createBevelBorder(BevelBorder.LOWERED);
        Border lineBorder = BorderFactory.createEtchedBorder();
        //Border lowerBevelBorder = BorderFactory.createLoweredBevelBorder();

        mainPanel = new JPanel();       // holds trackListPanel, bottomPanel & controlPanel
        trackListPanel = new JPanel();  // holds top-half (table)
        bottomPanel = new JPanel();     // holds bottom-half (includes output area)
        controlPanel = new JPanel();    // user GUI controls

        // Child Panels for controlPanel
        JPanel browseRemovePanel = new JPanel();
        JPanel genrePanel = new JPanel();
        JPanel patternPanel = new JPanel();
        JPanel autoFillPanel = new JPanel();
        JPanel uploadPanel = new JPanel();


        // ScrollPane & model data for JTable
        Vector<Vector<String>> tableModelData = new Vector<Vector<String>>();  // tempDataModel properties
        columns = new Vector();
        columns.add("File");
        columns.add("Track");
        columns.add("Artist");
        columns.add("Genre");
        //columns.add("Path");

        tableModel = new DefaultTableModel(tableModelData, columns);

        jTable = new JTable(tableModel) {
            public boolean isCellEditable(int row,int column){
                return (column != 3 && column != 0);   //the 4th column is not editable
            }
        };

        //jTable.removeColumn(jTable.getColumnModel().getColumn(4));

        jTable.setName("UploadJTable");

        //jTable.setColumnSelectionAllowed(false);
        //jTable.setRowSelectionAllowed(true);

        scrollPane = new JScrollPane(jTable);
        Dimension d = new Dimension(585, 110);  // use tableModelData.size() once populated

        jTable.getColumnModel().getColumn(0).setPreferredWidth(200);
        scrollPane.setPreferredSize(d);  // forces vertical scroll bar
        trackListPanel.add(scrollPane);

        // Output area
        Dimension outputDimension = new Dimension(220, 180);
        outputTextArea = new JEditorPane();
        outputTextArea.setEditable(false);
        outputTextArea.setContentType("text/html");
        outputTextArea.setPreferredSize(outputDimension);

        JScrollPane outScrollpane = new JScrollPane(outputTextArea);
        outScrollpane.setPreferredSize(outputDimension);
        //JPanel outputPanel = new JPanel();
        //outputPanel.add(outScrollpane);

        //outScrollpane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);


        //scrollPanePanel.add(outScrollpane);

        // Browse & remove Panel
        JButton browseButton = new JButton("Browse");
        JButton removeButton = new JButton("Remove");

        //browseRemovePanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        browseRemovePanel.add(browseButton);
        browseRemovePanel.add(removeButton);
       // browseRemovePanel.setBorder(lineBorder);

        // AutoFill Panel
        pattSplit1 = new JRadioButton("track-artist");
        pattSplit2 = new JRadioButton("artist-track");
        radioBtnGroup = new ButtonGroup();
        radioBtnGroup.add(pattSplit1);
        radioBtnGroup.add(pattSplit2);

        pattSplit1.addActionListener(this);
        pattSplit2.addActionListener(this);

        patternPanel.add(pattSplit1);
        patternPanel.add(pattSplit2);
        //patternPanel.setBorder(lineBorder);
        //patternPanel.add(patternButton);


        // Genre Panel
        //String[] comboBoxData = {"Genre", "Ambient", "Rock", "Dance", "Dub", "RnB", "Drum and bass", "Folk"};
        genreList = new JComboBox();
        //genreList.setPreferredSize(new Dimension(25, 150));

          String os = System.getProperty("os.name");

           boolean isWindows = os.contains("win");
           boolean isLinux = os.contains("nux") || os.contains("nix");
           boolean isMac = os.contains("mac");

        if(isMac ){
            genreList.setPrototypeDisplayValue("xxxxxx");
        } else {
            genreList.setPrototypeDisplayValue("xxxxxxxxxxx");
        }

        updateGenreList(new ArrayList());   // if it is completely empty, then exception is thrown when selecting table track with empty genre list

        genreNameTextField = new JTextField("Genre name");
        genreNameTextField.setBorder(bevelBorder);
        genreDescTextField = new JTextField("Genre description");
        genreDescTextField.setBorder(bevelBorder);
        genreNameTextField.setName("GenreNameTextField");
        genreDescTextField.setName("genreDescTextField");
        JButton genreBtn = new JButton("Add Genre");
        genreBtn.setPreferredSize(new Dimension(120, 24));
        genreNameTextField.setPreferredSize(new Dimension(90, 24));
        genreDescTextField.setPreferredSize(new Dimension(125, 24));
        //genreTextField.addActionListener(this);
        //genrePanel.add(genreList);
        genrePanel.add(genreNameTextField);
        genrePanel.add(genreDescTextField);
        genrePanel.add(genreBtn);
        //genrePanel.setBorder(lineBorder);

        //JButton autoFillBtn = new JButton("Auto fill");
        //autoFillPanel.add(genreList);
        //autoFillPanel.add(autoFillBtn);

        patternPanel.add(genreList);

        // Upload Panel
        Dimension btnD = new Dimension(150,25);
        uploadButton = new JButton("Upload");
        uploadButton.setPreferredSize(btnD);
        cancelButton = new JButton("Cancel");
        cancelButton.setPreferredSize(btnD);

        progressBar = new JProgressBar(0, 100);
        progressBar.setValue(0);
        progressBar.setStringPainted(true);
        progressBar.setString("0%");
        progressBar.setPreferredSize(new Dimension(190, 23));
        uploadPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        uploadPanel.add(uploadButton);
        cancelButton.setVisible(false);
        uploadPanel.add(cancelButton);
        uploadPanel.add(progressBar);
        //uploadPanel.setBorder(lineBorder);

        // Icons for all buttons
        try {
            URL resourceURL = getClass().getClassLoader().getResource(MFWindow.MEDIA+"remove_icon.png");
            Icon tmpIcon = new ImageIcon(resourceURL);
            removeButton.setIcon(tmpIcon);
            cancelButton.setIcon(tmpIcon);

            resourceURL = getClass().getClassLoader().getResource(MFWindow.MEDIA+"search_icon.png");
            tmpIcon = new ImageIcon(resourceURL);
            browseButton.setIcon(tmpIcon);

            resourceURL = getClass().getClassLoader().getResource(MFWindow.MEDIA+"upload_icon.png");
            tmpIcon = new ImageIcon(resourceURL);
            uploadButton.setIcon(tmpIcon);

            resourceURL = getClass().getClassLoader().getResource(MFWindow.MEDIA+"create_genre_icon.png");
            tmpIcon = new ImageIcon(resourceURL);
            genreBtn.setIcon(tmpIcon);

            resourceURL = getClass().getClassLoader().getResource(MFWindow.MEDIA+"complete_icon.png");
            tmpIcon = new ImageIcon(resourceURL);
            //autoFillBtn.setIcon(tmpIcon);

        } catch (Exception e) {
            ReportsPanel.log("Error: "+e);
        }

        controlPanel.setPreferredSize(new Dimension(360, 180));
        //controlPanel.add(new JSeparator(SwingConstants.HORIZONTAL));

        controlPanel.setLayout(new GridLayout(4, 1));
        controlPanel.setBorder(lineBorder);
        controlPanel.add(browseRemovePanel);
        controlPanel.add(patternPanel);
        //controlPanel.add(autoFillPanel);
        controlPanel.add(genrePanel);
        controlPanel.add(uploadPanel);
        //controlPanel.add(outScrollpane);

        //Box box = Box.createHorizontalBox();
        //bottomPanel.setLayout(new GridLayout(1,2));
        bottomPanel.add(controlPanel);
        bottomPanel.add(outScrollpane);
        //bottomPanel.setPreferredSize(new Dimension(585, 120));
        //bottomPanel.add(box);
        //box.add(Box.createRigidArea(new Dimension(10, 7)));

        //bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.X_AXIS));
        //bottomPanel.add(Box.createRigidArea(new Dimension(5,100)));
            //bottomPanel.add(controlPanel);
        //bottomPanel.add(Box.createRigidArea(new Dimension(5,100)));
            //bottomPanel.add(outScrollpane);
        //bottomPanel.add(Box.createRigidArea(new Dimension(5,100)));

        mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
        //mainPanel.setLayout(new GridLayout(2,1));
        mainPanel.add(trackListPanel);
        //mainPanel.add(controlPanel);
        //mainPanel.add(outScrollpane);
        mainPanel.add(bottomPanel);

        //mainPanel.add(Box.createRigidArea(new Dimension(10,100)));

        add(mainPanel);

        genreList.addActionListener(this);
        genreNameTextField.addActionListener(this);
        genreNameTextField.addMouseListener(this);
        genreDescTextField.addActionListener(this);
        genreDescTextField.addMouseListener(this);
        removeButton.addActionListener(this);
        browseButton.addActionListener(this);
        uploadButton.addActionListener(this);
        //cancelButton.addActionListener(this);
        genreBtn.addActionListener(this);
        //autoFillBtn.addActionListener(this);
        //scrollPane.addMouseListener(this);

        jTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
        jTable.addMouseListener(this);
        //jTable.addMouseListener(new PopClickListener());
    }


    public void refreshOuputArea(int[] selectedRows) {

        //int[] selectedRows = jTable.getSelectedRows();

        outputTextArea.setText("");     // non will be selected after user has removed n tracks.

        StringBuffer strHTMLBuffer = new StringBuffer();
        strHTMLBuffer.append("<html><font size=2 face='Courier New' >");

        DefaultTableModel tmpTableModel = (DefaultTableModel) jTable.getModel();
        Vector allTracks = tmpTableModel.getDataVector();

        int j = 0;

        for(j = 0; j < selectedRows.length; j++) {

            int selectedIndex = selectedRows[j];

            // chosenTracks: [187 Lockdown - Gunman (Natural Born Chillers Remix).mp3, , , ]
            Vector <String> subData = (Vector) allTracks.get(selectedIndex);

            strHTMLBuffer.append("<b>File:&nbsp;&nbsp;</b>"+subData.get(0) + "<br>");
            strHTMLBuffer.append("<b>Track:&nbsp;&nbsp;</b>"+subData.get(1) + "<br>");
            strHTMLBuffer.append("<b>Artist:&nbsp;&nbsp;</b>"+subData.get(2)+ "<br>");
            strHTMLBuffer.append("<b>Genre:&nbsp;&nbsp;</b>"+subData.get(3)+ "<br><br>");
        }

        if(j > 0) {
            strHTMLBuffer.append("</font></html>");
            outputTextArea.setText(strHTMLBuffer.toString());
        }
    }


    public void actionPerformed(ActionEvent actionEvent) {

        String strAction = actionEvent.getActionCommand();

        ReportsPanel.log("actionPerformed "+strAction);

        if(strAction == "Browse") {
            fileChooser = new JFileChooser();
            fileChooser.setMultiSelectionEnabled(true);
            int returnValue = fileChooser.showOpenDialog(null);
            File[] selectedfiles = fileChooser.getSelectedFiles();

            StringBuilder sb = new StringBuilder();

            DefaultTableModel tmpTableModel = (DefaultTableModel) jTable.getModel();

            Vector tableModelData = tmpTableModel.getDataVector();

            int i = 0;

            for (i = 0; i < selectedfiles.length; i++) {

                String strFile = selectedfiles[i].getName();
                String strFilePath = selectedfiles[i].getAbsolutePath();

                Vector <String> subData = new Vector<String>();

                subData.add(strFile);
                subData.add("");
                subData.add("");
                subData.add("");


                //Vector <String> tmpUploadFiles = (Vector) subData.clone();

                //Vector <String> tmpUploadFiles = new Vector();

                //tmpUploadFiles.remove(0);
                //tmpUploadFiles.add(0, strFilePath);

                uploadList.add(tableModelData.size(), strFilePath); // keep same order as TableModelData

                //ReportsPanel.log(uploadList.toString());

                tableModelData.add(subData);

                jTable.setModel(tableModel);
                //jTable.removeColumn(jTable.getColumnModel().getColumn(4));
                sb.append(strFile + "\n");
            }



            if(i>0) {
                tableModel.setDataVector(tableModelData, columns);
                jTable.getColumnModel().getColumn(0).setPreferredWidth(200);
            }

        } else if(strAction == "comboBoxChanged"){

            if(genreList.getSelectedIndex() > 0) {

                String currGenre = (String) genreList.getSelectedItem();

                int[] selectedRows = jTable.getSelectedRows();

                DefaultTableModel tmpTableModel = (DefaultTableModel) jTable.getModel();

                Vector chosenTracks = tmpTableModel.getDataVector();

                int k = 0;

                for(k = 0; k < selectedRows.length; k++) {

                    int selectedIndex = selectedRows[k];

                    Vector fileParts = (Vector) chosenTracks.get(selectedIndex);

                    ReportsPanel.log("fileParts "+fileParts.toString());
                    // passed by ref, so can keep hold of same vector and call setDataVector and setModel
                    fileParts.remove(3);
                    fileParts.add(3, currGenre);

                    tableModel.setDataVector(chosenTracks, columns);
                }

                if(k > 0) {

                    jTable.setModel(tableModel);
                    refreshOuputArea(selectedRows);
                    jTable.getColumnModel().getColumn(0).setPreferredWidth(200);

                    for(k = 0; k < selectedRows.length; k++) {
                        jTable.addRowSelectionInterval(selectedRows[k],selectedRows[k]);
                    }
                }
            }

        } else if(strAction == "Remove") {

            int[] selectedRows = jTable.getSelectedRows();

            DefaultTableModel tmpTableModel = (DefaultTableModel) jTable.getModel();
            Vector tableModelData = tmpTableModel.getDataVector();

            int j = 0;

            for(j = 0; j < selectedRows.length; j++) {

                int selectedIndex = selectedRows[j];
                tableModelData.remove(selectedIndex);
                uploadList.remove(selectedIndex);

                tableModel.setDataVector(tableModelData, columns);
            }

            //ReportsPanel.log(uploadList.toString());

            if(j > 0) {
                jTable.setModel(tableModel);
                jTable.getColumnModel().getColumn(0).setPreferredWidth(200);
                //refreshOuputArea(selectedRows);
                outputTextArea.setText("");
            } else {
                JOptionPane.showMessageDialog(this,"Select songs for removal", "Error", JOptionPane.ERROR_MESSAGE);
            }

        } else if(strAction == "artist-track" || strAction == "track-artist") {

            int[] selectedRows = jTable.getSelectedRows();

            DefaultTableModel tmpTableModel = (DefaultTableModel) jTable.getModel();

            Vector chosenTracks = tmpTableModel.getDataVector();

            //String fileExts[] = {".mp3", ".wav", ".m4a"};

            for(int k = 0; k < selectedRows.length; k++) {

                int selectedIndex = selectedRows[k];

                Vector fileParts = (Vector) chosenTracks.get(selectedIndex);

                String strFile = (String) fileParts.toArray()[0];
                String strTrack = (String) fileParts.toArray()[1];
                String strArtist = (String) fileParts.toArray()[2];
                String strGenre = (String) fileParts.toArray()[3];
                //String strPath = (String) fileParts.toArray()[4];

                //ReportsPanel.log("strPath "+strPath);

                //strFile = strFile.trim();

                String absPath = (String) uploadList.get(selectedIndex);
                File file = new File(absPath);

                //if(!validFilePath(file)) {  // probably dont need to do this, as we do it later

                //} else {    // have to check that end data is valid, result of this splitting

                    // TODO use proper reg exp and split on file extension too?
                    // TODO remove file extension from "Track" str
                    // TODO what if file name doesn't match e.g. "01 To Sick To Pray (dont call the do.mp3"
                    if(pattSplit1.isSelected()) {   // track - artist.mp3
                        strTrack = strFile.split("-")[0].trim();
                        strArtist = strFile.split("-")[1].trim();

                         for(int j = 0; j < fileExts.length; j++) {
                            if(strArtist.indexOf(fileExts[j]) > -1) {
                                strArtist  = strArtist.split(fileExts[j])[0].trim();
                                j = fileExts.length;
                            }
                        }
                    } else if(pattSplit2.isSelected()) {    // artist - track.ext
                        strTrack = strFile.split("-")[1].trim();
                        strArtist = strFile.split("-")[0].trim();

                        for(int j = 0; j < fileExts.length; j++) {
                            if(strTrack.indexOf(fileExts[j]) > -1) {
                            strTrack  = strTrack.split(fileExts[j])[0].trim();
                            j = fileExts.length;
                        }
                    }

                    } else {
                        ReportsPanel.log("Please select a file pattern");
                    }
                //}

                fileParts.removeAllElements();

                // passed by ref, so can keep hold of same vector and call setDataVector and setModel
                fileParts.add(0, strFile);
                fileParts.add(1, strTrack);
                fileParts.add(2, strArtist);
                fileParts.add(3, strGenre);
                //fileParts.add(4, strPath);

                tableModel.setDataVector(chosenTracks, columns);

            }

            refreshOuputArea(selectedRows); // ok to be before call to setModel?
            jTable.setModel(tableModel);
            jTable.getColumnModel().getColumn(0).setPreferredWidth(200);

            for(int k = 0; k < selectedRows.length; k++) {
                jTable.addRowSelectionInterval(selectedRows[k],selectedRows[k]);
            }

        } else if(strAction == "Upload") {

            PostData postData = new PostData();

            int[] selectedRows = jTable.getSelectedRows();

            ReportsPanel.log("selectedRows "+selectedRows.toString());
            ReportsPanel.log("selectedRows len "+selectedRows.length);

            // *****************************
            // USE RELATIVE PATHS FOR TMP UPLOAD AND DOWNLOAD DATA. Dont know users system
            // *****************************

            //String uploadFilename = "/home/sam/mf_server_dump/music_finder_uploads/"+File.separator+"client_uploadfile.zip";
            //String uploadFilename = ".."+File.separator+"client_uploadfile.zip";
            String uploadFilename = "client_uploadfile.zip";

            ReportsPanel.log("uploadFilename "+uploadFilename);

            ZipOutputStream zipOutputStream = null;
            FileInputStream fileInputStream = null;

            StringBuilder strReq = new StringBuilder();

            strReq.append("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>"+"\n");
            strReq.append("<MFRequest>"+"\n");
            strReq.append("<Request type='Upload'/>"+"\n");
            strReq.append("<Uploads>"+"\n");

            progressBar.setValue(0);
            progressBar.setStringPainted(true);
            progressBar.setString("0%");


            try {

                if(selectedRows.length > 0) {

                    ReportsPanel.log("selectedRows.length > 0");

                    //zipOutputStream = new ZipOutputStream(new FileOutputStream(new File(uploadFilename)));
                    int j;

                    for(j = 0; j < selectedRows.length; j++) {

                        int selectedIndex = selectedRows[j];

                        DefaultTableModel tmpTableModel = (DefaultTableModel) jTable.getModel();

                        Vector chosenTracks = tmpTableModel.getDataVector();

                        Vector subData = (Vector) chosenTracks.get(selectedIndex);

                        String strFile = (String) subData.toArray()[0];
                        String strTrack = (String) subData.toArray()[1];
                        String strArtist = (String) subData.toArray()[2];
                        String strGenre = (String) subData.toArray()[3];

                        String strType = getFileExtension(strFile);

                        String absPath = (String) uploadList.get(selectedIndex);
                        File soundFile = new File(absPath);
                        /*
                        AudioInputStream audioInputStream = null;

                        ReportsPanel.log("soundFile "+soundFile.getName());

                        // javax.sound.sampled.UnsupportedAudioFileException: could not get audio input stream from input file
                        // http://java.sun.com/products/java-media/sound/list.html
                        // http://stackoverflow.com/questions/8624803/facing-issue-in-loading-a-mp3-file-in-java-getting-could-not-get-audio-input-str
                        // http://www.velocityreviews.com/forums/t149533-mp3-playback-using-java-sound-and-sun-mp3-plugin.html

                        try {
                            audioInputStream = AudioSystem.getAudioInputStream(soundFile);
                        } catch (UnsupportedAudioFileException e) {
                            e.printStackTrace();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }

                        AudioFormat format = audioInputStream.getFormat();
                        ReportsPanel.log("AudioFormat "+format);
                        long audioFileLength = soundFile.length();
                        ReportsPanel.log("audioFileLength "+audioFileLength);
                        int frameSize = format.getFrameSize();
                        float frameRate = format.getFrameRate();
                        float durationInSeconds = (audioFileLength / (frameSize * frameRate));
                        ReportsPanel.log("durationInSeconds "+durationInSeconds);
                        */

                        if(uploadDataValid(soundFile, strTrack, strArtist, strGenre)) {

                            if(zipOutputStream == null ){
                                zipOutputStream = new ZipOutputStream(new FileOutputStream(new File(uploadFilename)));
                            }
                            strReq.append("<Track file='"+strFile+"' name='"+strTrack+"' artist='"+strArtist+"' genre='"+strGenre+"' type='"+strType+"'/>"+"\n");

                            ReportsPanel.log("file='"+strFile+"' name='"+strTrack+"' artist='"+strArtist+"' genre='"+strGenre+"' type='"+strType+"'");

                            //String absPath = (String) uploadList.get(selectedIndex);

                            //File file = new File(absPath);

                            // getAbsolutePath()      // otherwise get dirs created on server
                            ZipEntry zippedTrack = new ZipEntry(soundFile.getName()); // could just use file name from TableModelData. File obj not used.

                            zipOutputStream.putNextEntry(zippedTrack);  // seems to "prepare" output stream for writing to?

                            fileInputStream = new FileInputStream(absPath);

                            // Transfer bytes from the file to the ZIP file
                            int BYTES_DOWNLOAD = 1024;
                            int read=0;
                            int bytesTotal = 0;

                            byte[] bytes = new byte[BYTES_DOWNLOAD];

                            ReportsPanel.log("absPath "+absPath);
                            ReportsPanel.log("Writing ZipEntry "+zippedTrack.getName() + " to " + uploadFilename);

                            ReportsPanel.log("zippedTrack.getCompressedSize() " + zippedTrack.getCompressedSize());
                            ReportsPanel.log("zippedTrack.getSize() " + zippedTrack.getSize());

                            ReportsPanel.log("Writing ZipEntry "+absPath);
                            ReportsPanel.log("Entry compressed size: "+zippedTrack.getCompressedSize());
                            ReportsPanel.log("Entry size: "+zippedTrack.getSize());

                            while ((read = fileInputStream.read(bytes)) > 0) {  // InputStream for the file to read from
                                zipOutputStream.write(bytes, 0, read);          // OutputStream for the file to write to
                                bytesTotal+= read;
                            }

                            ReportsPanel.log("bytesTotal: "+ bytesTotal);

                            ReportsPanel.log("bytesTotal "+bytesTotal);
                        } else {
                            ReportsPanel.log("Upload data not valid");
                        }
                    }

                    strReq.append("</Uploads>"+"\n");
                    strReq.append("</MFRequest>"+"\n");

                    ReportsPanel.log("J "+j);
                    ReportsPanel.log("rows "+selectedRows.length);
                    ReportsPanel.log("zipOutputStream "+zipOutputStream);

                    if(zipOutputStream != null && j == selectedRows.length) {
                            ReportsPanel.log(strReq.toString());
                            uploadButton.setVisible(false);
                            cancelButton.setVisible(true);
                            cancelButton.setEnabled(false);
                            System.out.println("UploadPanel.postData.uploadFiles()");
                            System.out.println(strReq.toString() + ", "+uploadFilename);
                            postData.uploadFiles(strReq.toString(), uploadFilename, this);
                            //new File(uploadFilename).delete();
                            zipOutputStream.close();
                        } else{
                        ReportsPanel.log("Error: zipOutputStream != null && j == selectedRows.length-1");
                    }


                } else {
                    JOptionPane.showMessageDialog(this,"Select songs for upload", "Error", JOptionPane.ERROR_MESSAGE);
                }

            } catch( Exception e) {
                ReportsPanel.log(e.toString());
                e.printStackTrace();
            }

        } else if(strAction == "Add Genre") {

            String newGenre = genreNameTextField.getText().trim();
            String genreDesc = genreDescTextField.getText().trim();

            newGenre = newGenre.trim(); // should just ber one word and don't want spaces etc

            if(newGenre.equals("Genre name") || newGenre.equals("")) {

                JOptionPane.showMessageDialog(this,"Enter a genre name.", "Error", JOptionPane.ERROR_MESSAGE);
                //genreNameTextField.setBackground(Color.RED);
                genreNameTextField.setText("Genre name");

            } else if(genreDesc.equals("Genre description") || genreDesc.equals("")) {

                JOptionPane.showMessageDialog(this,"Enter a genre description.", "Error", JOptionPane.ERROR_MESSAGE);
                //genreDescTextField.setBackground(Color.RED);
                genreDescTextField.setText("Genre description");

            } else {

                String genreError = "";

                String GENRE_PATTERN = "^([a-zA-Z\\s]+)$";  // shouldnt really allow spaces in genre name, but do trim() above

                Pattern wordPattern = Pattern.compile(GENRE_PATTERN, Pattern.CASE_INSENSITIVE);
                Matcher wordMatcher;

                wordMatcher = wordPattern.matcher(newGenre);

                if(!wordMatcher.matches()){
                    genreError = "Genre name has illegal characters.";
                }

                wordMatcher = wordPattern.matcher(genreDesc);

                if(!wordMatcher.matches()){
                    genreError += "\nGenre description has illegal characters.";
                }

                if(genreError.equals("")) {

                    PostData postData = new PostData();

                    StringBuilder strReq = new StringBuilder();

                    strReq.append("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>"+"\n");
                    strReq.append("<MFRequest>"+"\n");
                    strReq.append("<Request type='AddGenre' data='"+newGenre+"'/>"+"\n");
                    strReq.append("<Genre name='"+newGenre+"' description='"+genreDesc+"'/>"+"\n");
                    strReq.append("</MFRequest>"+"\n");

                    Document genreResponse = postData.sendDataForWriteReponse(strReq.toString());

                    NodeList nList = genreResponse.getElementsByTagName("Response");
                    Node nNode = nList.item(0);
                    Element eElement = (Element) nNode;
                    String type = eElement.getAttribute("type");

                        /**
                         * <MFResponse>
                         *     <Response type='Error' code='MF_USER_NOT_FOUND'>
                         *         </Response>
                         * </MFResponse>
                         */

                        if(type.equals("Error")) {
                            String errorCode = eElement.getAttribute("code");

                            if(errorCode.equals("GENRE_CONFLICT")) {

                               JOptionPane.showMessageDialog(this,"Genre name already exists.", "Error", JOptionPane.ERROR_MESSAGE);
                            }else {
                                JOptionPane.showMessageDialog(this,"Unknown error adding genre.", "Error", JOptionPane.ERROR_MESSAGE);
                            }
                        }else if(type.equals("Catagories")) {
                            // this also calls updateGenreList()
                            ArrayList getGenreData = downloadControlPanel.populateGenreTable(genreResponse);
                            //updateGenreList(getGenreData);
                            JOptionPane.showMessageDialog(this,"Genre successfully added", "Info", JOptionPane.INFORMATION_MESSAGE);
                        }
                } else {
                    JOptionPane.showMessageDialog(this,genreError, "Error", JOptionPane.ERROR_MESSAGE);
                }
            }
        }
    }

    private String getFileExtension(String strFile) {
        for(int j = 0; j < fileExts.length; j++) {

            String format = fileExts[j];

             if(strFile.indexOf(format) > -1) {
                 return format;
             }
        }

        // TODO unsupported file type
        return "";
    }

    private boolean validFileFormat(String strFile) {

        if(strFile.indexOf("-") > -1 || strFile.split("-")[0].trim().length() > 0 || strFile.split("-")[1].trim().length() > 0 ) {
            return true;
        }

        for(int j = 0; j < fileExts.length; j++) {
             if(strFile.indexOf(fileExts[j]) > -1) {
                 return true;
             }
        }

        return false;
    }

    private boolean uploadDataValid(File file, String strTrack, String strArtist, String strGenre) {

        String strFile = file.getAbsolutePath();

        ReportsPanel.log("uploadDataValid "+ strFile +", " +strTrack +", " + strArtist + ", " + strGenre);

        String errorMsg = "";
        // Check user has filled out all required data

        if(strFile.length() == 0 ) {
            errorMsg = "No file provided";
        }else if( strTrack.length() == 0) {
            errorMsg = "No track name provided";
        }else if(strArtist.length() == 0) {
            errorMsg = "No artist name provided";
        }else if(strGenre.length() == 0) {
            errorMsg = "No genre provided";
        }

        if(!errorMsg.equals("")) {
            JOptionPane.showMessageDialog(this,errorMsg, "Error", JOptionPane.ERROR_MESSAGE);
            return false;
        }

        // Check we can read/see the file
        if(!file.exists()) {
            JOptionPane.showMessageDialog(this, "Unable to read target file", "Error", JOptionPane.ERROR_MESSAGE);
            return false;
        }

        // Remove and replace any duplicate or single whitespace with a single escaped white space
        strTrack = strTrack.replaceAll("\\s+", "\\ ");
        strArtist = strArtist.replaceAll("\\s+", "\\ ");

        // Allow      "-"     "("      ")"      "."      " "      a-z      0-9      for artist and track
        //String SANITIZE_DB_PATTERN = "^(?:[\\w])([a-z_\\-\\s0-9\\s\\.()]+)$";
        // removed pattern and \\w just checking for a word, artist and name isnt going to start with slashes
        String SANITIZE_DB_PATTERN = "^([a-zA-Z_\\-\\s0-9\\s\\.()]+)$";

        Pattern dbPattern = Pattern.compile(SANITIZE_DB_PATTERN, Pattern.CASE_INSENSITIVE);
        Matcher dbMatcher;

        dbMatcher = dbPattern.matcher(strTrack);

        if(!dbMatcher.matches()){
            JOptionPane.showMessageDialog(this, "Track name has illegal characters", "Error", JOptionPane.ERROR_MESSAGE);
            return false;
        }

        dbMatcher = dbPattern.matcher(strArtist);

        if(!dbMatcher.matches()){
            JOptionPane.showMessageDialog(this, "Artist name has illegal characters", "Error", JOptionPane.ERROR_MESSAGE);
            return false;
        }

        // Check for valid file path
        boolean validFilePath = true;//validFilePath(file);

        if(!validFilePath) {
            JOptionPane.showMessageDialog(this,"File path has illegal characters", "Error", JOptionPane.ERROR_MESSAGE);
        }
        return validFilePath;
    }

    private boolean validFilePath(File file){

        // Check for illegal chars and correct file path
        FileValidator fileValidator = new FileValidator();
        boolean valid = fileValidator.validate(file.getAbsolutePath());

        ReportsPanel.log("validFilePath "+valid);
        return valid;
    }

    class FileValidator {

       private Pattern patternWin;
       private Matcher matcherWin;

       private Pattern patternLin;
       private Matcher matcherLin;

        // added \s for white space and \- for hyphen to both patterns

        // Windows paths
       private final String WINDOWS_FILE_PATTERN = "^(?:[\\w]\\:|\\\\)(\\\\[a-z_\\-\\s0-9\\s\\-\\.()]+)+\\.(mp3|wav|m4a|m4p|wma)$";
       // String target = "C:\\foo.mp3";

        // Linux paths
       private final String LINUX_FILE_PATTERN = "^(?:[\\w]|/)(/|[a-z_/-/s0-9\\s\\-\\.()]+)+\\.(mp3|wav|m4a|m4p|wma)$";
       // String target = "/home/sam/foo.mp3";

       public FileValidator(){
          patternWin = Pattern.compile(WINDOWS_FILE_PATTERN, Pattern.CASE_INSENSITIVE);
          patternLin = Pattern.compile(LINUX_FILE_PATTERN, Pattern.CASE_INSENSITIVE);
       }

       public boolean validate(final String upload){

           String os = System.getProperty("os.name");

           boolean isWindows = os.contains("win");
           boolean isLinux = os.contains("nux") || os.contains("nix");
           boolean isMac = os.contains("mac");

           ReportsPanel.log("OS: "+os);

           ReportsPanel.log("target file path: "+upload);

           if(isWindows) {

               matcherWin = patternWin.matcher(upload);
               return matcherWin.matches();

           } else if(isLinux || isMac){

               matcherLin = patternLin.matcher(upload);
               ReportsPanel.log("matcherLin.matches() "+matcherLin.matches());

               return matcherLin.matches();
           }

           return false;
       }
    }

    public void updateProgressBar(long progress) {
        progressBar.setString(progress + "%");
        progressBar.setValue((int)progress);
    }

    public void uploadComplete() {

        //progressBar.setString("0%");
        //progressBar.setValue(0);
        ReportsPanel.log("UploadPanel.uploadComplete ");
        jTable.clearSelection();
        uploadButton.setVisible(true);
        cancelButton.setVisible(false);

        if(SettingsPanel.USER_SETTING_CLEAR_UPLOADS.equals("yes")) {
            DefaultTableModel tmpTableModel = (DefaultTableModel) jTable.getModel();

            Vector tableModelData = tmpTableModel.getDataVector();

            tableModelData.clear();

            tableModel.setDataVector(tableModelData, columns);

            //jTable.setModel(tableModel);
        }

        progressBar.setString("0%");
        progressBar.setValue(0);
    }

    public void updateGenreList(ArrayList genres) {

        ReportsPanel.log("updateGenreList "+genres.toArray());

        Vector comboData = new Vector();

        Genre tmpG = new Genre("Genres...","","","");

        genres.add(0, tmpG);

        for(int i = 0; i < genres.size(); i++){

            Genre genre = (Genre) genres.get(i);

            comboData.add(i, genre.getName());
        }

        genreList.setModel(new DefaultComboBoxModel(comboData));
    }

    public void mouseClicked(MouseEvent mouseEvent) {

        String componentName = mouseEvent.getComponent().getName();

        if(componentName.equals("GenreNameTextField")) {

            //genreNameTextField.setBackground(Color.WHITE);

            if(genreNameTextField.getText().equals("Genre name")) {
                genreNameTextField.setText("");
            }

        } else if(componentName.equals("genreDescTextField")) {

            //genreDescTextField.setBackground(Color.WHITE);

            if(genreDescTextField.getText().equals("Genre description")) {
                genreDescTextField.setText("");
            }

        } else if(componentName.equals("UploadJTable")) {

            if (SwingUtilities.isLeftMouseButton(mouseEvent)) {

            }
            else if (SwingUtilities.isRightMouseButton(mouseEvent)) {

                Point p = mouseEvent.getPoint();    // get the coordinates of the mouse click

                int rowNumber = jTable.rowAtPoint( p ); // get the row index that contains that coordinate

                PopUpDemo menu = new PopUpDemo(rowNumber+1);
                menu.show(mouseEvent.getComponent(), mouseEvent.getX(), mouseEvent.getY());
            }

            //if(genreList != null & genreList.getModel() != null) {
                refreshOuputArea(jTable.getSelectedRows());
                genreList.setSelectedIndex(0);  // WARNING: fires ComboBox listener > hence then refreshes upload scroll panel
                radioBtnGroup.clearSelection();
            //}
        }
    }

    class PopUpDemo extends JPopupMenu {

        JMenuItem anItem;

        public PopUpDemo(int rowNum){
            anItem = new JMenuItem("Delete row "+rowNum);
            add(anItem);
            anItem = new JMenuItem("Deselect All Rows");
            add(anItem);
            anItem = new JMenuItem("Delete Selected Rows");
            add(anItem);
        }
    }

    class PopClickListener extends MouseAdapter {
        public void mousePressed(MouseEvent e){
            if (e.isPopupTrigger()) {
                //doPop(e);
            }
        }

        public void mouseReleased(MouseEvent e){
            if (e.isPopupTrigger()) {
                //doPop(e);
            }

        }

        //private void doPop(MouseEvent e){
            //PopUpDemo menu = new PopUpDemo();
            //menu.show(e.getComponent(), e.getX(), e.getY());
        //}
    }



    public void mousePressed(MouseEvent mouseEvent) {}

    public void mouseReleased(MouseEvent mouseEvent) {}

    public void mouseEntered(MouseEvent mouseEvent) {}

    public void mouseExited(MouseEvent mouseEvent) {}
}
